﻿using IOP.AspNetCore.Extension.Http;
using IOP.AspNetCore.Extension.Upload;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace IOP.AspNetCore.Extension.UploadServer
{
    /// <summary>
    /// 上传服务中间件
    /// </summary>
    public class UploadServerMiddleware
    {
        /// <summary>
        /// 中间件委托
        /// </summary>
        private RequestDelegate _next;

        /// <summary>
        /// 上传配置
        /// </summary>
        private UploadOption _option;

        /// <summary>
        /// 日志
        /// </summary>
        private ILogger<UploadServerMiddleware> _logger;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        /// <param name="option"></param>
        /// <param name="logger"></param>
        public UploadServerMiddleware(RequestDelegate next, ILogger<UploadServerMiddleware> logger, UploadOption option)
        {
            _next = next;
            _option = option;
            _logger = logger;
        }

        /// <summary>
        /// 执行中间件逻辑
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path.Equals(_option.UploadUrl, StringComparison.Ordinal))
            {
                if (context.Request.Method != "POST")
                {
                    await context.ReturnBadRequestAsync("请使用POST方式执行请求", 500);
                    return;
                }
                if (_option.PhysicPath == "")
                    throw new ArgumentNullException("PhysicPath", "文件存放路径不能为空");
                else
                {
                    if (_option.CustomFileHandle != null)
                        _option.CustomFileHandle.Invoke(context);
                    else
                    {
                        try
                        {
                            string rootPath = _option.PhysicPath;
                            string requestRoot = _option.FileRequestPath;
                            StringValues label = context.Request.Form["path"];
                            if (label != StringValues.Empty)
                            {
                                string[] paths = label.ToString().Split('/');
                                var localPath = Path.Combine(paths);
                                rootPath = Path.Combine(rootPath, localPath);
                                requestRoot += label;
                            }
                            if (!Directory.Exists(rootPath))
                                Directory.CreateDirectory(rootPath);
                            var files = context.Request.Form.Files;
                            List<string> filesPath = new List<string>();
                            Exception exception = null;
                            ParallelLoopResult result = Parallel.ForEach(files, async file =>
                            {
                                string extension = Path.GetExtension(file.FileName);
                                string newName = Guid.NewGuid().ToString("N") + extension;
                                string request = requestRoot + $"/{newName}";
                                string filePath = Path.Combine(rootPath, newName);
                                filesPath.Add(request);
                                try
                                {
                                    await file.UploadFile(filePath);
                                }
                                catch (Exception e)
                                {
                                    exception = e;
                                }
                            });
                            if (exception != null)
                                throw exception;
                            if (result.IsCompleted)
                            {
                                await context.ReturnSuccessAsync("upload success", filesPath);
                                return;
                            }
                        }
                        catch (Exception e)
                        {
                            _logger.LogError(e.Message + e.StackTrace);
                            await context.ReturnBadRequestAsync(e.Message, 500);
                            return;
                        }
                    }
                }
            }
            else await _next(context);
        }
    }
}
